package cn.sunxiansheng.web.config;

import cn.sunxiansheng.tool.response.ResultWrapper;
import cn.sunxiansheng.web.annotation.IgnoredResultWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Description: 装饰器
 *
 * @Author sun
 * @Create 2025/1/6 14:40
 * @Version 1.0
 */
@Slf4j
public class ReturnValueHandlersDecorator implements InitializingBean {

    /**
     * 注入适配器，可以获取
     */
    @Resource
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @Override
    public void afterPropertiesSet() {
        List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();
        // 因为默认List<HandlerMethodReturnValueHandler>是不可修改列表，所以需要给他转换成一个可修改的列表
        assert returnValueHandlers != null;
        List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);
        // 装饰一下然后重新设置
        this.decorateHandlers(handlers);
        // 将装饰后的handlers替换原来的不可变列表
        requestMappingHandlerAdapter.setReturnValueHandlers(handlers);
    }

    private void decorateHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
            if (returnValueHandler instanceof RequestResponseBodyMethodProcessor) {
                // 找到RequestResponseBodyMethodProcessor
                ControllerReturnValueHandler controllerReturnValueHandler = new ControllerReturnValueHandler(returnValueHandler);
                // 找到在原列表中的索引位置
                int index = returnValueHandlers.indexOf(returnValueHandler);
                // 将装饰后的HandlerMethodReturnValueHandler放到原来的位置
                returnValueHandlers.set(index, controllerReturnValueHandler);
            }
        }
    }

    private static class ControllerReturnValueHandler implements HandlerMethodReturnValueHandler {

        // 拿到被装饰的对象
        private final HandlerMethodReturnValueHandler handler;

        public ControllerReturnValueHandler(HandlerMethodReturnValueHandler handler) {
            this.handler = handler;
        }

        /**
         * 保持跟原生的一致
         *
         * @param returnType
         * @return
         */
        @Override
        public boolean supportsReturnType(MethodParameter returnType) {
            return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class);
        }

        @Override
        public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception {
            IgnoredResultWrapper methodAnnotation = methodParameter.getMethodAnnotation(IgnoredResultWrapper.class);
            // 如果有IgnoredResultWrapper注解，就不进行包装，走原来的逻辑
            if (Objects.nonNull(methodAnnotation)) {
                handler.handleReturnValue(o, methodParameter, modelAndViewContainer, nativeWebRequest);
                return;
            }
            // 如果返回值是ResultWrapper类型，也不进行包装，走原来的逻辑
            if (o instanceof ResultWrapper) {
                handler.handleReturnValue(o, methodParameter, modelAndViewContainer, nativeWebRequest);
                return;
            }
            // 其余情况，进行包装
            log.info("Controller返回值已被自动包装，如果上传文件，请加@IgnoredResultWrapper注解取消自动包装！");
            ResultWrapper<Object> ok = ResultWrapper.ok(o);
            handler.handleReturnValue(ok, methodParameter, modelAndViewContainer, nativeWebRequest);
        }
    }
}